package product

import (
	"context"
	"fmt"
	"strconv"
	"strings"

	"mall/common/ctxdatax"
	"mall/common/globalkey"
	"mall/common/moneyx"
	"mall/common/resultx"
	"mall/common/utils/pointy"
	"mall/service/forum/api/internal/logic/util"
	"mall/service/forum/api/internal/svc"
	"mall/service/forum/api/internal/types"
	"mall/service/forum/api/internal/utils/parse"
	"mall/service/product/rpc/productclient"

	"github.com/sirupsen/logrus"
	"github.com/zeromicro/go-zero/core/logx"
)

type GetProductListLogic struct {
	logx.Logger
	ctx    context.Context
	svcCtx *svc.ServiceContext
}

func NewGetProductListLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetProductListLogic {
	return &GetProductListLogic{
		Logger: logx.WithContext(ctx),
		ctx:    ctx,
		svcCtx: svcCtx,
	}
}

func (l *GetProductListLogic) GetProductList(req *types.ProductListReq) (*types.ProductListInfo, error) {
	userId := ctxdatax.GetUserId(l.ctx)
	if userId == 0 {
		return nil, resultx.NewErrCode(resultx.TOKEN_EXPIRE_ERROR)
	}

	page, pageSize := parse.ParsePageInfo(
		req.Current,
		req.PageSize,
		l.svcCtx.Config.Query.DefaultAmount,
		l.svcCtx.Config.Query.MaxAmount,
	)

	preload := "product:Id,ParentId,Title,Slug,Excerpt,Status"
	eagerLoad := "product:productMedia,productRelationship,productTerm,productMeta"
	preload += ";productMedia:Id,Uid,Url,Thumbnail,Order"
	preload += ";productRelationship:PerfumeId,PerfumeName,PerfumeSlug,BrandId,BrandName,BrandSlug"
	preload += ";productTerm:Id,Name,Slug,ParentId"
	preload += ";productMeta:mediaId,mediaUrl,showStock,stockUnit,averageRating,gemPriceMin,gemPriceMax,retailPriceMin,retailPriceMax,stock"

	var preFilter string
	var preFilterEntity string
	if req.ParentId != nil && *req.ParentId > 0 { // 指定香水ID
		preload += ";productAttribute:Id,IsSku,Name,Slug,HasImage"
		preload += ";productVariation:Id,GemPrice,RetailPrice,CounterPrice,Weight"
		preload += ";productVariationMeta:stock,mediaId,mediaUrl,sku,skuKey,saleCount,totalSaleCount"
		eagerLoad += ",productAttribute,productVariation,productVariationMeta,total"
		preFilter = "PerfumeId,equalTo:" + strconv.FormatInt(int64(*req.ParentId), 10)
		preFilterEntity = "productRelationship"
	} else if req.BrandId != nil && *req.BrandId > 0 {
		preFilter = "BrandId,equalTo:" + strconv.FormatInt(int64(*req.BrandId), 10)
		preFilterEntity = "productRelationship"
	}

	var filters []string
	if req.Filter != nil && len(strings.TrimSpace(*req.Filter)) > 0 {
		filterMap := util.GetFilterMap(strings.TrimSpace(*req.Filter))
		for key, val := range filterMap {
			switch key {
			case "sticky": // todo: 使用场景未知
				filters = append(filters, "sticky,equalTo:"+val)
			case "virtual": // todo: 使用场景未知
				filters = append(filters, "virtual,equalTo:"+val)
			}
		}
	}
	var keyword string
	if req.Title != nil && len(strings.TrimSpace(*req.Title)) > 0 {
		filters = append(filters, "Title,contain:"+strings.TrimSpace(*req.Title))
		keyword = strings.TrimSpace(*req.Title)
	}
	if req.Slug != nil && len(strings.TrimSpace(*req.Slug)) > 0 {
		filters = append(filters, "Slug,contain:"+strings.TrimSpace(*req.Slug))
		if len(keyword) == 0 {
			keyword = strings.TrimSpace(*req.Slug)
		}
	}
	if len(filters) == 0 {
		filters = append(filters, "virtual,equalTo:0")
	}

	var sorter *string
	if req.Sorter != nil {
		sorter = pointy.GetPointer(strings.TrimSpace(*req.Sorter))
	}
	var platformId uint32 = globalkey.ThirdPartyPlatformWechatMini

	res, err := l.svcCtx.ProductRpc.GetProductList(l.ctx, &productclient.BaseListReq{
		Offset:          uint32((page - 1) * pageSize),
		Limit:           uint32(pageSize),
		Preload:         pointy.GetPointer(preload),
		EagerLoad:       pointy.GetPointer(eagerLoad),
		Filter:          pointy.GetPointer(strings.Join(filters, ";")),
		Sorter:          sorter,
		PreFilterEntity: pointy.GetPointer(preFilterEntity),
		PreFilter:       pointy.GetPointer(preFilter),
		VisitorId:       pointy.GetPointer(userId),
		PlatformId:      pointy.GetPointer(platformId),
	})
	if err != nil {
		return nil, err
	}

	var hasNextPage bool
	var hasPreviousPage bool
	if res.HasNextPage != nil && *res.HasNextPage > 0 {
		hasNextPage = true
	}
	if res.HasPreviousPage != nil && *res.HasPreviousPage > 0 {
		hasPreviousPage = true
	}
	resp := &types.ProductListInfo{
		BaseListInfo: types.BaseListInfo{
			Total:           res.Total,
			Current:         page,
			PageSize:        pageSize,
			HasNextPage:     hasNextPage,
			HasPreviousPage: hasPreviousPage,
		},
	}

	if len(res.List) > 0 {
		for _, item := range res.List {
			var categories []*types.CategoryInfo
			if len(item.Categories) > 0 {
				for _, v := range item.Categories {
					categories = append(categories, &types.CategoryInfo{
						Id:       v.Id,
						Name:     v.Name,
						Slug:     v.Slug,
						ParentId: v.ParentId,
					})
				}
			}
			var tags []*types.TagInfo
			if len(item.Tags) > 0 {
				for _, v := range item.Tags {
					tags = append(tags, &types.TagInfo{
						Id:   v.Id,
						Name: v.Name,
						Slug: v.Slug,
					})
				}
			}

			var sellPoint *string
			var gemPriceMin *string
			var gemPriceMax *string
			var retailPriceMin *string
			var retailPriceMax *string
			var stock *uint32
			var stockUnit *string
			var showStock *bool
			var isNew *bool
			var isHot *bool
			var saleCount *uint32
			var reviewCount *uint32
			var ratingCount *uint32
			var averageRating *string
			var meta []*types.MetaInfo
			if len(item.Meta) > 0 {
				for _, v := range item.Meta {
					if v.Key == nil || v.Value == nil {
						continue
					}

					val := *v.Value
					switch *v.Key {
					case "gemPriceMin":
						gemPriceMin = pointy.GetPointer(val)
					case "gemPriceMax":
						gemPriceMax = pointy.GetPointer(val)
					case "retailPriceMin":
						retailPriceMin = pointy.GetPointer(val)
					case "retailPriceMax":
						retailPriceMax = pointy.GetPointer(val)
					case "saleCount":
						if i, err := strconv.ParseInt(val, 10, 32); err == nil {
							saleCount = pointy.GetPointer(uint32(i))
						}
					case "reviewCount":
						if i, err := strconv.ParseInt(val, 10, 32); err == nil {
							reviewCount = pointy.GetPointer(uint32(i))
						}
					case "ratingCount":
						if i, err := strconv.ParseInt(val, 10, 32); err == nil {
							ratingCount = pointy.GetPointer(uint32(i))
						}
					case "averageRating":
						if f, err := strconv.ParseFloat(val, 64); err == nil {
							averageRating = pointy.GetPointer(moneyx.FormatStar(f, 1))
						}
					case "isNew":
						if val == "1" {
							isNew = pointy.GetPointer(true)
						}
					case "isHot":
						if val == "1" {
							isHot = pointy.GetPointer(true)
						}
					case "stock":
						if i, err := strconv.ParseInt(val, 10, 32); err == nil {
							stock = pointy.GetPointer(uint32(i))
						}
					default:
						meta = append(meta, &types.MetaInfo{
							Id:    v.Id,
							Key:   v.Key,
							Value: v.Value,
						})
					}
				}
			}

			var brand *types.KbEntryInfo
			var perfume *types.KbEntryInfo
			if len(item.Relations) > 0 {
				for _, v := range item.Relations {
					if v.Key != nil {
						switch *v.Key {
						case globalkey.ProductRelationNameBrand:
							brand = &types.KbEntryInfo{
								Id:    v.Id,
								Title: v.Name,
								Slug:  v.Slug,
							}
						case globalkey.ProductRelationNamePerfume:
							perfume = &types.KbEntryInfo{
								Id:    v.Id,
								Title: v.Name,
								Slug:  v.Slug,
							}
						}
					}
				}
			}

			var photos []*types.MediaInfo
			if len(item.Media) > 0 {
				for _, v := range item.Media {
					var url string
					if v.Url != nil && len(*v.Url) > 0 {
						url = *v.Url
					}
					if len(url) == 0 && v.Uri != nil && len(*v.Uri) > 0 {
						url = *v.Uri
					}

					var thumbnail *bool
					if v.Thumbnail != nil {
						thumbnail = pointy.GetPointer(*v.Thumbnail > 0)
					}

					photos = append(photos, &types.MediaInfo{
						Uid:       v.Uid,
						Id:        v.Id,
						Thumbnail: thumbnail,
						Order:     v.Order,
						Url:       pointy.GetPointer(url),
						AltText:   v.AltText,
						MetaData:  v.MetaData,
					})
				}
			}

			var attrs []*types.ProductAttrInfo
			if len(item.Attrs) > 0 {
				for _, v := range item.Attrs {
					if len(v.Options) > 0 {
						var options []*types.ProductAttrOptInfo
						for _, opt := range v.Options {
							options = append(options, &types.ProductAttrOptInfo{
								Id:       opt.Id,
								Label:    opt.Label,
								Slug:     opt.Slug,
								MediaId:  opt.MediaId,
								MediaUrl: opt.MediaUrl,
							})
						}
						var hasMedia *bool
						if v.HasMedia != nil && *v.HasMedia > 0 {
							hasMedia = pointy.GetPointer(true)
						}
						attrs = append(attrs, &types.ProductAttrInfo{
							Id:       v.Id,
							Label:    v.Label,
							Slug:     v.Slug,
							HasMedia: hasMedia,
							Options:  options,
						})
					}
				}
			}

			var specs []*types.ProductAttrInfo
			idSpecOptMap := make(map[string]*types.ProductAttrOptInfo)
			idSpecMap := make(map[string]*productclient.ProductAttrInfo)
			if len(item.Specs) > 0 {
				for _, v := range item.Specs {
					if len(v.Options) > 0 {
						var options []*types.ProductAttrOptInfo
						for _, opt := range v.Options {
							if opt.Id == nil {
								continue
							}

							optId := strconv.FormatInt(int64(*opt.Id), 10)
							option := &types.ProductAttrOptInfo{
								Id:       opt.Id,
								Label:    opt.Label,
								Slug:     opt.Slug,
								MediaId:  opt.MediaId,
								MediaUrl: opt.MediaUrl,
							}
							idSpecOptMap[optId] = option
							options = append(options, option)
							idSpecMap[optId] = v
						}
						var hasMedia *bool
						if v.HasMedia != nil && *v.HasMedia > 0 {
							hasMedia = pointy.GetPointer(true)
						}
						specs = append(specs, &types.ProductAttrInfo{
							Id:       v.Id,
							Label:    v.Label,
							Slug:     v.Slug,
							HasMedia: hasMedia,
							Options:  options,
						})
					}
				}
			}

			var skus []*types.ProductVariationInfo
			if len(item.Skus) > 0 {
				for _, v := range item.Skus {
					if v.Key == nil {
						continue
					}

					var optKeys []string
					var specs []*types.ProductAttrInfo
					if keys := strings.Split(*v.Key, ","); len(keys) > 0 {
						for _, optKey := range keys {
							if spec, ok := idSpecMap[optKey]; ok {
								if opt, ok := idSpecOptMap[optKey]; ok {
									optKey = "opt_" + optKey
									optKeys = append(optKeys, optKey)
									opt.Key = pointy.GetPointer(optKey)
									options := []*types.ProductAttrOptInfo{opt}
									var hasMedia *bool
									if spec.HasMedia != nil && *spec.HasMedia > 0 {
										hasMedia = pointy.GetPointer(true)
									}
									var specKey *string
									if spec.Id != nil {
										specKey = pointy.GetPointer("spec_" + strconv.FormatInt(int64(*spec.Id), 10))
									}
									specs = append(specs, &types.ProductAttrInfo{
										Id:       spec.Id,
										Key:      specKey,
										Label:    spec.Label,
										Slug:     spec.Slug,
										HasMedia: hasMedia,
										Options:  options,
									})
								}
							}
						}
					}
					skus = append(skus, &types.ProductVariationInfo{
						Id:           v.Id,
						Key:          pointy.GetPointer(strings.Join(optKeys, "_-_")),
						GemPrice:     v.GemPrice,
						RetailPrice:  v.RetailPrice,
						CounterPrice: v.CounterPrice,
						Inventory:    v.Inventory,
						Weight:       v.Weight,
						SaleCount:    v.SaleCount,
						// MediaId:      v.MediaId,
						MediaUrl: v.MediaUrl,
						Specs:    specs,
					})
				}
			}

			var statusText *string
			if item.Status != nil {
				statusText = pointy.GetPointer(util.GetStatusText(int32(*item.Status)))
			}

			resp.List = append(resp.List, &types.ProductInfo{
				Id:             item.Id,
				Title:          item.Title,
				Slug:           item.Slug,
				Excerpt:        item.Excerpt,
				ContentHtml:    item.Content,
				Status:         item.Status,
				StatusText:     statusText,
				Brand:          brand,
				Perfume:        perfume,
				Categories:     categories,
				Tags:           tags,
				Media:          photos,
				Meta:           meta,
				Attrs:          attrs,
				Specs:          specs,
				Skus:           skus,
				SellPoint:      sellPoint,
				GemPriceMin:    gemPriceMin,
				GemPriceMax:    gemPriceMax,
				RetailPriceMin: retailPriceMin,
				RetailPriceMax: retailPriceMax,
				Stock:          stock,
				StockUnit:      stockUnit,
				ShowStock:      showStock,
				IsNew:          isNew,
				IsHot:          isHot,
				SaleCount:      saleCount,
				ReviewCount:    reviewCount,
				RatingCount:    ratingCount,
				AverageRating:  averageRating,
			})
		}

		if page == 1 && len(keyword) > 0 {
			logRes, err := l.svcCtx.ProductRpc.SearchLog(l.ctx, &productclient.SearchLogReq{
				Keyword:    pointy.GetPointer(keyword),
				VisitorId:  pointy.GetPointer(userId),
				PlatformId: pointy.GetPointer(platformId),
			})
			if err != nil {
				logrus.Info(fmt.Sprintf("GetProductList SearchLog err: %+v", err))
			} else {
				resp.SearchId = pointy.GetPointer(int64(logRes.Id))
			}
		}
	}

	return resp, nil
}
